package com.franklin.java.service.database.ui;

import com.franklin.java.api.ui.MessageActionListener;
import com.franklin.java.api.ui.jpanel.button.MyButton;
import com.franklin.java.api.ui.jpanel.combobox.MyComboBox;
import com.franklin.java.api.ui.table.AbstractTableModel;
import com.franklin.java.entity.CostTime;
import com.franklin.java.entity.sql.JdbcInfo;
import com.franklin.java.icon.MybatisIcons;
import com.franklin.java.service.database.JdbcService;
import com.franklin.java.service.database.JdbcState;
import com.franklin.java.ui.PluginToolWindow;
import com.franklin.java.ui.ToolWindowContent;
import com.franklin.java.utils.SwingUtils;
import com.franklin.plugins.common.collection.MyComparator;
import com.franklin.plugins.common.collection.PrettyArrayList;
import com.franklin.plugins.common.utils.DataCheckUtils;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.InputValidator;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentFactory;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * @author Franklin
 * @since 2021/9/25 16:42
 */
public class JdbcResultForm extends ToolWindowContent {
    private JPanel mainPanel;
    private JTabbedPane windowTab;
    private JTable resultTable;
    private MyButton redoButton;
    private JPanel sqlPanel;
    private MyComboBox<String> pageSizeComboBox;
    private MyButton firstPageButton;
    private MyButton previousPageButton;
    private MyButton nextPageButton;
    private MyButton lastPageButton;
    /**
     * 详情面板
     */
    private JPanel detailPanel;

    private final Project project;
    private final String resultTab = "Result";
    private Integer pageIndex = 0;
    private List<SqlConsoleForm> sqlConsoleFormList = new PrettyArrayList<>();

    private final AtomicReference<String> lastSqlReference = new AtomicReference<>("");
    private final AtomicReference<String> removeSqlReference = new AtomicReference<>("");
    private String lastPageSize = "100";

    public JdbcResultForm(Project project) {
        this.project = project;
        this.content = createContent();
        init();
    }

    private void createUIComponents() {
        // TODO: place custom component creation code here
        this.pageSizeComboBox = new MyComboBox<>();
    }

    JPanel getDetailPanel() {
        return detailPanel;
    }

    private void init() {
        initPageSizeComboBox();
        initFirstPageButton();
        initLastPageButton();
        initPreviousPageButton();
        initNextPageButton();
        initRedoButton();
    }

    private void initRedoButton() {
        this.redoButton.addActionListener(
            new ChangePageActionListener() {
                @Override
                protected void doChangePage() {
                    //do nothing
                }
            }
        );
    }

    private void initPageSizeComboBox() {
        this.pageSizeComboBox.addItem("10");
        this.pageSizeComboBox.addItem("100");
        this.pageSizeComboBox.addItem("250");
        this.pageSizeComboBox.addItem("500");
        this.pageSizeComboBox.addItem("1000");
        this.pageSizeComboBox.addItem("All");
        this.pageSizeComboBox.addItem("Custom");
        this.pageSizeComboBox.setSelectedItem("100");
        this.pageSizeComboBox.addActionListener(
            new ChangePageActionListener() {
                @Override
                protected void doChangePage() {
                    String selectedItem = (String) pageSizeComboBox.getSelectedItem();
                    if (selectedItem.equals("Custom")) {
                        String customValue = SwingUtils.showInputDialog(
                            "Input your page size : ",
                            "Change Page Size",
                            "100",
                            new InputValidator() {
                                @Override
                                public boolean checkInput(String inputString) {
                                    return DataCheckUtils.isInteger(inputString);
                                }

                                @Override
                                public boolean canClose(String inputString) {
                                    return DataCheckUtils.isInteger(inputString);
                                }
                            }
                        );
                        int value = Integer.parseInt(customValue);
                        String pageSize = String.valueOf(value);
                        pageSizeComboBox.addItem(pageSize);
                        pageSizeComboBox.setSelectedItem(pageSize);
                    }
                    String newPageSize = pageSizeComboBox.getSelectedItem().toString();
                    if (!newPageSize.equals(lastPageSize)){
                        lastPageSize = newPageSize;
                        pageIndex = 0;
                    }
                }
            }
        );
    }

    private CostTime runLastSql(String sql) throws SQLException {
        JdbcInfo jdbcInfo = JdbcState.getLastJdbcInfo();
        if (Objects.isNull(jdbcInfo)) {
            return CostTime.ZERO;
        }
        if (DataCheckUtils.isEmpty(sql)) {
            return CostTime.ZERO;
        }
        JdbcService jdbcService = JdbcService.getInstance();
        jdbcService.addDataSource(jdbcInfo);

        //设置limit

        try {
            return jdbcService.executeQuery(
                jdbcInfo,
                sql + getLimit(),
                showResultInWindow(sql)
            );
        } catch (Exception exception) {
            exception.printStackTrace();
            throw new RuntimeException("Sql execute failed");
        }
    }

    public String getLimit() {
        String selectedItem = (String) this.pageSizeComboBox.getSelectedItem();
        if (DataCheckUtils.isEmpty(selectedItem)) {
            return "";
        }
        if (selectedItem.equals("All")) {
            return "";
        } else if (selectedItem.equals("Custom")) {
            return " \n\tLimit 100";
        } else {
            //计算分页参数
            //limit pageIndex * pageSize ,  pageSize;
            Integer pageSize = DataCheckUtils.parseInt(selectedItem, 100);
            int start = pageIndex * pageSize;
            return " \n\tLimit " + start + " , " + pageSize;
        }
    }

    private void initNextPageButton() {
        this.nextPageButton.setIcon(MybatisIcons.RunSql.playNext);
        this.nextPageButton.addActionListener(
            new ChangePageActionListener() {
                @Override
                protected void doChangePage() {
                    pageIndex++;
                }
            }
        );
    }

    private void initPreviousPageButton() {
        this.previousPageButton.setIcon(MybatisIcons.RunSql.playPrevious);
        this.previousPageButton.addActionListener(
            new ChangePageActionListener() {
                @Override
                protected void doChangePage() {
                    if (pageIndex > 0) {
                        pageIndex--;
                    }
                }
            }
        );
    }

    private void initLastPageButton() {
        this.lastPageButton.setIcon(MybatisIcons.RunSql.playLast);
        // TODO: 2021/9/26 实现最后一页功能
    }

    private void initFirstPageButton() {
        this.firstPageButton.setIcon(MybatisIcons.RunSql.playFirst);
        this.firstPageButton.addActionListener(
            new ChangePageActionListener() {
                @Override
                protected void doChangePage() {
                    pageIndex = 0;
                }
            }
        );
    }

    public static JdbcResultForm getInstance(Project project) {
        return ServiceManager.getService(project, JdbcResultForm.class);
    }

    @Override
    public JPanel getMainPanel() {
        return mainPanel;
    }

    public void addSql(String sql,CostTime costTime) {
        this.pageIndex = 0;
        this.lastSqlReference.set(sql);
        Component[] components = this.sqlPanel.getComponents();
        this.sqlPanel.removeAll();
        this.sqlPanel.setLayout(SwingUtils.createLayoutManager(components.length + 1, 1));
        SqlConsoleForm sqlConsoleForm = new SqlConsoleForm(project, sql, this.showResultInWindow(sql), this);
        this.sqlConsoleFormList.add(sqlConsoleForm);
        SwingUtils.showTestMessage(sqlConsoleForm.getDetailPanel(),true,costTime.toString());
        MyButton removeSqlButton = sqlConsoleForm.getRemoveSqlButton();
        removeSqlButton.addActionListener(e -> {
            sqlConsoleForm.remove();
            removeSql(sql);
        });
        AtomicInteger rowIndex = new AtomicInteger(0);
        Dimension size = new Dimension(1500, 300);
        this.sqlConsoleFormList = this.sqlConsoleFormList.stream()
            .sorted(MyComparator.comparingDesc(SqlConsoleForm::getTime))
            .peek(consoleForm -> this.addCell(this.sqlPanel,consoleForm.getMainPanel(),rowIndex.getAndIncrement(),0,size))
            .collect(Collectors.toList());
    }

    public void removeSql(String sql) {
        this.sqlPanel.removeAll();
        this.sqlPanel.setLayout(SwingUtils.createLayoutManager(this.sqlConsoleFormList.size(), 1));
        AtomicInteger rowIndex = new AtomicInteger(0);
        Dimension size = new Dimension(1500, 300);
        this.sqlConsoleFormList = this.sqlConsoleFormList.stream()
            .sorted(MyComparator.comparingDesc(SqlConsoleForm::getTime))
            .peek(sqlConsoleForm ->  {
                if (sqlConsoleForm.isRemove()){
                    this.removeSqlReference.set(sql);
                }else {
                    this.addCell(this.sqlPanel, sqlConsoleForm.getMainPanel(), rowIndex.getAndIncrement(), 0, size);
                }
            })
            .filter(sqlConsoleForm -> !sqlConsoleForm.isRemove())
            .collect(Collectors.toList());
    }

    /**
     * 将ResultSet结果输出到ResultTable<br>
     *
     * @param :
     * @return java.util.function.Consumer<java.sql.ResultSet>
     * @author Franklin
     * @since 2021/9/25 17:43
     */
    public BiConsumer<ResultSet,CostTime> showResultInWindow(String sql) {
        return (resultSet,costTime) -> {
            this.resultTable.removeAll();
            try {
                ResultSetMetaData metaData = resultSet.getMetaData();
                int columnCount = metaData.getColumnCount();
                String[] headers = new String[columnCount];
                for (int i = 0; i < columnCount; i++) {
                    headers[i] = metaData.getColumnName(i + 1);
                }
                AbstractTableModel<String> sqlResultModel = new AbstractTableModel<>() {
                    @Override
                    protected String[] headerNames() {
                        return headers;
                    }

                    @Override
                    protected Object[] toArray(String rowData) {
                        return new Object[0];
                    }

                    @Override
                    protected <E> void setVal(String rowData, int columnIndex, E value) {

                    }
                };
                int rowCount = 0;
                while (resultSet.next()) {
                    Object[] rowDataArr = new Object[columnCount];
                    for (int i = 0; i < columnCount; i++) {
                        rowDataArr[i] = resultSet.getObject(i + 1);
                    }
                    sqlResultModel.addRow(rowDataArr);
                    rowCount++;
                }

                this.windowTab.setTitleAt(1, String.format(this.resultTab + " (%s rows)", rowCount));
                SwingUtils.showTestMessage(this.detailPanel,true,costTime.toString());
                this.resultTable.setModel(sqlResultModel);
                String lastSql = lastSqlReference.get();
                if (!lastSql.equals(sql)) {
                    this.addSql(sql,costTime);
                }else if (sql.equals(removeSqlReference.get())){
                    removeSqlReference.set("");
                    this.addSql(sql,costTime);
                }

                if (!isVisible()) {
                    PluginToolWindow pluginToolWindow = PluginToolWindow.getInstance(project);
                    pluginToolWindow.addContent(this);
                    pluginToolWindow.show();
                }
            } catch (SQLException exception) {
                exception.printStackTrace();
            }

        };
    }

    @NotNull
    @Override
    protected Content createContent() {
        ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();
        Content mybatisSqlConsole = contentFactory.createContent(this.getMainPanel(), "Mybatis Sql Console", false);
        mybatisSqlConsole.setCloseable(true);
        mybatisSqlConsole.setIcon(MybatisIcons.logo);
        return mybatisSqlConsole;
    }

    /**
     * 页面跳转ActionListener
     */
    private abstract class ChangePageActionListener extends MessageActionListener<CostTime> {

        protected ChangePageActionListener() {
            super("", true, detailPanel);
        }

        @Override
        protected CostTime handleActionEvent(ActionEvent e) throws Exception {
            doChangePage();
            if (DataCheckUtils.isNotEmpty(sqlConsoleFormList)){
                addShowMessagePanel(sqlConsoleFormList.get(0).getDetailPanel());
            }
            return runLastSql(lastSqlReference.get());
        }

        /**
         * 修改分页参数
         */
        protected abstract void doChangePage();

        @Override
        protected void lastOperation() {
            if (this.showMessagePanels.size() == 2){
                this.showMessagePanels.remove(1);
            }
        }
    }
}
